<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT">





  <link rel="alternate" href="/atom.xml" title="renhao" type="application/atom+xml">






<meta name="description" content="本文总结下面试中常被问到的 vue 问题。">
<meta property="og:type" content="article">
<meta property="og:title" content="前端面试总结 vue 部分">
<meta property="og:url" content="https://huangrenhao.gitee.io/2020/09/24/vue-interview/index.html">
<meta property="og:site_name" content="renhao">
<meta property="og:description" content="本文总结下面试中常被问到的 vue 问题。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2020-09-24T06:47:49.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="前端面试总结 vue 部分">
<meta name="twitter:description" content="本文总结下面试中常被问到的 vue 问题。">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://huangrenhao.gitee.io/2020/09/24/vue-interview/">





  <title>前端面试总结 vue 部分 | renhao</title>
  








<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>

<body itemscope="" itemtype="http://schema.org/WebPage" lang="zh-Hans">
  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope="" itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">renhao</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope="" itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://huangrenhao.gitee.io/2020/09/24/vue-interview/">

    <span hidden itemprop="author" itemscope="" itemtype="http://schema.org/Person">
      <meta itemprop="name" content="renhao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="renhao">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">前端面试总结 vue 部分</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-09-24T09:51:01+08:00">
                2020-09-24
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope="" itemtype="http://schema.org/Thing">
                  <a href="/categories/interview/" itemprop="url" rel="index">
                    <span itemprop="name">interview</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2020/09/24/vue-interview/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count valine-comment-count" data-xid="/2020/09/24/vue-interview/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  7.5k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  27
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>本文总结下面试中常被问到的 vue 问题。 <a id="more"></a></p>
<h3 id="如何理解-vue-双向绑定（响应式原理、依赖收集）"><a href="#如何理解-vue-双向绑定（响应式原理、依赖收集）" class="headerlink" title="如何理解 vue 双向绑定（响应式原理、依赖收集）"></a>如何理解 vue 双向绑定（响应式原理、依赖收集）</h3><p>如 input 标签，加上 <code>v-model</code> 之后，input 输入值，相应的 v-model 绑定的值也会同步更新，这个被绑定的值需要放在 data 中。</p>
<p>vue 数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的，可以通过打印 data 中的数据得知 vue 是通过<code>Object.definePropetry()</code>来实现数据劫持的（有自定义的 get 和 set 方法）。</p>
<p>mvvm 主要包含两个方面，数据变化更新视图、视图变化更新数据，view更新 data 可以通过事件监听实现，data 更新 view 的重点是如何得知数据改变，可以通过 <code>Object.defineProperty()</code>对属性设置一个 set 函数，当数据改变就触发这个函数，只要将一些需要更新的方法放到这里面就可以实现 data 更新 view。</p>
<h4 id="实现过程"><a href="#实现过程" class="headerlink" title="实现过程"></a>实现过程</h4><ul>
<li><p>监听器 Observer：用来监听所有属性，如果属性发生变化就需要告诉订阅者 Watcher 看是否需要更新</p>
</li>
<li><p>订阅者 Watcher：可以收到属性的变化通知并执行相应的函数从而更新视图</p>
</li>
<li><p>消息订阅器 Dep：因为订阅者有很多个，需要专门收集这些订阅者，然后在监听器 observer 和订阅者 watcher 之间进行统一管理。</p>
<blockquote>
<p>Dep 可以防止在 data 中定义但未在实际模板中使用到的变量，如使用<code>this.dataKey = value</code> 时触发到 setter 导致重新执行渲染，因为取值时触发 getter 事件，在最开始render 所有被渲染所依赖的</p>
</blockquote>
</li>
<li><p>指令解析器 Compile：对每个结点元素进行扫描和解析，将相关指令初始化成一个订阅者 Watcher，并替换模板数据或者绑定相应的函数</p>
</li>
</ul>
<p>数组的 push 无法通过 defineProperty 完成监听：源码中是通过对数组的原型上的方法如push、prop、shift、splice、sort、reverse、unshift进行重新包装。</p>
<p>Object.defineProperty 的缺点：</p>
<ul>
<li>无法监听对象非已有属性的添加和已有属性的删除，只会对对象原有的全部属性进行数据劫持（显示在页面上就是 bug）</li>
<li>数组对象也不能通过属性或者索引控制数组（如 length、index 实现响应式）</li>
</ul>
<p>解决以上两种缺陷可以使用 <code>Vue.set(target,key,value)</code>，即修改数据的时候使用 Vue.set，这个函数中对于数组采用 splice，对象就重新监听相应属性。</p>
<h5 id="属性遍历劫持到对象劫持"><a href="#属性遍历劫持到对象劫持" class="headerlink" title="属性遍历劫持到对象劫持"></a>属性遍历劫持到对象劫持</h5><p>Vue 3.0 版本将基于 Proxy 来改造观察者模式（现在还是3.0试用版），Proxy 再目标对象之前进行”拦截“，外界对该对象的访问都必须通过这层连接，故可对外界的访问进行过滤和改写，支持数组和对象操作。</p>
<h4 id="computed-如何实现响应式"><a href="#computed-如何实现响应式" class="headerlink" title="computed 如何实现响应式"></a>computed 如何实现响应式</h4><p>initComputed 中给 computed 中的每个 key 对应的值都生成了一个 Watcher 对象，会将 watcher 的 dirty 设置为 this.lazy（<code>{lazy:true}</code>），以减少计算量。 然后利用 Object.defineProperty 给计算属性对应的 key 值添加 getter、setter，这个 getter 是一个 computedGetter 函数，即获取 vm 实例上的 computed 属性时就会触发 computedGeter 方法，这个方法中：当第一次访问 computed 中的值时因为 <code>watcher.dirty = watcher.lazy</code> 的原因调用 evalute 方法，这个方法中会调用 watcher实例的 get 方法并设置 dirty 为 false，这个 get 方法中首先调用 pushTarget 方法（将 Dep.target 设置为所传入的 watcher），<strong>然后调用</strong> <strong>getter</strong> <strong>方法</strong>（即computed[key]，用户传入的 computed 方法，如果方法里面用到了 this.data 的值或者其他被用 defineReactive 包装过的对象，就会访问被代理的该属性的 get 方法，即 observer 中定义的 get 方法，其中 Dep.target 被设置为 当前的 computed 的 watcher，故可得知当前 computed watcher 依赖 this.data 的 dep，这样通过 this.data 代理的 set 方法中调用 notify 就可以更新所有依赖 this.data 的 computed 属性的 value）</p>
<p>相比与 data 的响应式：首先遍历 data，给 data 中的对象通过 <code>Object.defineProperty</code> 添加 get、set 劫持，然后再 new Watcher，其中会有 this.value = this.get()，这个 get 方法中会调用 pushTarget 方法，<strong>然后调用 getter 访问 data 中的数据</strong>（会调用 data 属性值的 get），就会调用 dep.depend() 将当前的 dep 添加到该 watcher 的 newDeps ，同时将 wacher 添加到 dep 的 subs 数组中。数据 set 时会调用 dep.notify()，会遍历 watcher 并执行 watcher.update()，其中会将该 watcher 添加到观察者队列中，会在 nextTick 中执行 watcher.run()，这个 watcher.run 中执行了我们定义 watcher 时传入的 cb 进行更新。</p>
<h4 id="vuex-的响应式"><a href="#vuex-的响应式" class="headerlink" title="vuex 的响应式"></a>vuex 的响应式</h4><p>传入的state作为一个隐藏的vue组件的data,也就是说，我们的commit操作，本质上其实是修改这个组件的data值，结合上文的computed,修改被<strong>defineReactive</strong>代理的对象值后，会将其收集到的依赖的<strong>watcher</strong>中的<strong>dirty</strong>设置为true,等到下一次访问该watcher中的值后重新获取最新值。</p>
<blockquote>
<ul>
<li><a href="https://www.cnblogs.com/chenhuichao/p/10818396.html" target="_blank" rel="noopener">简单双向绑定 vue 的实现</a></li>
<li><a href="https://segmentfault.com/q/1010000016056813" target="_blank" rel="noopener">vue set 和 $set 原理</a></li>
<li><a href="https://segmentfault.com/a/1190000020363006?utm_source=tag-newest" target="_blank" rel="noopener">vue 从 defineProperty 到 proxy</a></li>
<li><a href="https://es6.ruanyifeng.com/#docs/proxy" target="_blank" rel="noopener">阮一峰 es6 proxy</a></li>
<li><a href="https://www.jianshu.com/p/d95a7b8afa06" target="_blank" rel="noopener">vuex 工作原理详解</a></li>
<li><a href="https://www.jianshu.com/p/ff4c97cbf05a" target="_blank" rel="noopener">vue 源码响应式原理 compute</a> </li>
</ul>
</blockquote>
<h3 id="对于-MVVM-的理解"><a href="#对于-MVVM-的理解" class="headerlink" title="对于 MVVM 的理解"></a>对于 MVVM 的理解</h3><p>Model 代表数据模型，View 代表 UI 组件，ViewModel 监听模型数据的改变和控制视图行为、处理用户交互，ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来，而 View 和 Model 之间的同步工作是自动的，无需人为干涉。</p>
<h3 id="vue-DOM异步更新"><a href="#vue-DOM异步更新" class="headerlink" title="vue DOM异步更新"></a>vue DOM异步更新</h3><p>vue 中的 DOM 更新是异步的。</p>
<blockquote>
<p>Vue官方文档解释：</p>
<p>Vue 异步执行 DOM 更新。只要观察到数据变化，Vue 将开启一个队列，并缓冲在同一事件循环中发生的所有数据改变。<strong>如果同一个 watcher 被多次触发，只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要</strong>（异步更新优点）。然后，在下一个的事件循环“tick”中，Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 <code>Promise.then</code> 和<code>MessageChannel</code>，如果执行环境不支持，会采用 <code>setTimeout(fn, 0)</code>代替。</p>
<p>例如，当你设置<code>vm.someData = &#39;new value&#39;</code>，该组件不会立即重新渲染。当刷新队列时，组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程，但是如果你想在 DOM 状态更新后做点什么，这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考，避免直接接触 DOM，但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ，可以在数据变化之后立即使用<code>Vue.nextTick(callback)</code> 。这样回调函数在 DOM 更新完成后就会调用。</p>
</blockquote>
<p>从 watch 的源码中可以看到 update 其实是异步的，添加观察者队列后在 nextTick 中进行更新。</p>
<blockquote>
<p><a href="https://segmentfault.com/a/1190000013314893" target="_blank" rel="noopener">参考</a></p>
</blockquote>
<h3 id="nexttick-函数"><a href="#nexttick-函数" class="headerlink" title="nexttick 函数"></a>nexttick 函数</h3><p>应用场景：</p>
<ul>
<li>在 vue 生命周期的 created() 钩子函数中进行的　DOM　操作一定要放在<code>Vue.nextTick()</code> 的回调函数中，因为在<code>created()</code>钩子函数执行的时候DOM 其实并未进行任何渲染，而此时进行DOM操作无异于徒劳，所以此处一定要将DOM操作的js代码放进<code>Vue.nextTick()</code>的回调函数中。与之对应的就是<code>mounted()</code>钩子函数，因为该钩子函数执行时所有的DOM挂载和渲染都已完成，此时在该钩子函数中进行任何DOM操作都不会有问题 </li>
<li>在数据变化后要执行的某个操作，而这个操作需要使用随数据改变而改变的 DOM 结构的时候，这个操作都应该放在 <code>Vue.nextTick()</code> 的回调函数中。</li>
</ul>
<p>nextTick 中就是将传入的回调写成异步任务的方式，对异步做了兼容。</p>
<ul>
<li><p>Vue 2.4 版本之前：其中有 promise、MutationObserver、setTimeout 等方式。</p>
<blockquote>
<p>MutationObserver 是 HTML5 中的新 API，用来监视 DOM 变动的接口，能监听一个 DOM 对象上发生的子节点删除、属性修改、文本内容修改等等。</p>
<pre class=" language-javascript"><code class="language-javascript"><span class="token keyword">var</span> mo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutationObserver</span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// 先绑回调，这个回调会在 MutationObserver 实例监听到变动时触发</span>
<span class="token keyword">var</span> domTarget <span class="token operator">=</span> 你想要监听的dom节点
mo<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span>domTarget<span class="token punctuation">,</span> <span class="token punctuation">{</span>
   characterData<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token comment" spellcheck="true">//说明监听文本内容的修改。</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
</blockquote>
</li>
<li><p>vue 2.5 之后：<code>nextTick</code>采取的策略是默认走微任务，对于一些<code>DOM</code>的交互事件，如<code>v-on</code>绑定的事件回调处理函数的处理，会强制走宏任务。原因是微任务执行优先级非常高，某些场景下甚至比事件冒泡还要快。</p>
<p>模拟宏任务会按顺序检测原生 setImmediate、MessageChannel、setTimeout。</p>
<blockquote>
<p>MessageChannel：允许我们创建一个新的消息通道，并通过它的两个<code>MessagePort</code>属性发送数据</p>
<p>MessageChannel 优先于 setTimeout 是因为 setTimeout 最小时间延迟是 4ms，为了让回调异步尽早调用。</p>
</blockquote>
<p>模拟异步任务相较于以前版本舍弃了 MutationObserver（兼容性）。</p>
</li>
</ul>
<h3 id="vue-的渲染过程（vue-模板到浏览器可以解读的文件过程）"><a href="#vue-的渲染过程（vue-模板到浏览器可以解读的文件过程）" class="headerlink" title="vue 的渲染过程（vue 模板到浏览器可以解读的文件过程）"></a>vue 的渲染过程（vue 模板到浏览器可以解读的文件过程）</h3><p>new Vue：调用 _init 函数，</p>
<ul>
<li><p>进行初始化（包括生命周期、事件、render 函数、state）、</p>
</li>
<li><p><code>$mount</code>组件。<code>$mount</code>过程中将 template 编译成 render function（包含编译器版本的vue）</p>
<ul>
<li>将模板 template 进行 parse 得到 AST 语法树（正则等方式解析 template 模板中指令、css、style等）</li>
<li>然后利用optimize 进行优化，更新界面时会有一个 patch 过程，diff 算法会直接跳过静态节点，减少比较过程。</li>
<li>最后 generate 将 AST语法树转化成 render function 字符串。</li>
</ul>
</li>
<li><p>通过 <code>vm._render</code> 会得到虚拟 dom ，将其作为参数传递给 <code>vm._update</code>执行，_update 中会将新旧两个 VNode 进行一次 patch 过程得到 VNode 的最小差异，然后将这些差异渲染到视图上。</p>
<p>patch 核心 diff 算法，通过同层的树节点进行比较而非对树进行逐层搜索遍历。</p>
</li>
<li><p>映射到真实 dom，vue 为平台做了一层适配层，因为虚拟 dom 可以在任何支持 js 语言的平台上操作，不同的平台之间通过适配层对外提供相同的接口。</p>
</li>
</ul>
<blockquote>
<p>参考：</p>
<ul>
<li><a href="https://www.jianshu.com/p/2da2c0fd0619" target="_blank" rel="noopener">从 template 到 DOM</a></li>
<li><a href="https://blog.csdn.net/chenzhizhuo/article/details/101291652" target="_blank" rel="noopener">csdn：虚拟 dom 到真实 dom</a></li>
</ul>
</blockquote>
<h3 id="vue-的生命周期"><a href="#vue-的生命周期" class="headerlink" title="vue 的生命周期"></a>vue 的生命周期</h3><p>vue 实例从创建到销毁的过程就是生命周期，总共分为 8 个阶段：创建前/后、载入前/后、更新前/后、销毁前/后。</p>
<p><strong>beforeCreate</strong>（创建前） 生成<code>$options</code>选项，并给实例添加生命周期相关属性。在实例初始化之后，在 数据观测(data observer) 和event/watcher 事件配置之前被调用，也就是说，data，watcher，methods都不存在这个阶段。但是有一个对象存在，那就是<code>$route</code>，因此此阶段就可以根据路由信息进行重定向等操作。<br><strong>created</strong>（创建后） 初始化与依赖注入相关的操作，会遍历传入methods的选项，初始化选项数据，从<code>$options</code>获取数据选项(vm.$options.data)，给数据添加‘观察器’对象并创建观察器，定义getter、setter存储器属性。在实例创建之后被调用，该阶段可以访问data，使用watcher、events、methods，也就是说 数据观测(data observer) 和event/watcher 事件配置 已完成。但是此时dom还没有被挂载。该阶段允许执行http请求操作。</p>
<p><strong>beforeMount</strong> 将HTML解析生成AST节点，再根据AST节点动态生成渲染函数。<strong>相关render函数首次被调用</strong>(划重点)</p>
<p><strong>mounted</strong>（载入后）在挂载完成之后被调用，执行render函数生成虚拟dom，创建真实dom替换虚拟dom，并挂载到实例。可以操作dom，比如事件监听。此过程中进行ajax交互。<br><strong>beforeUpdate</strong>（更新前） 在数据更新之前调用，发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态，不会触发附加的重渲染过程。<br><strong>updated</strong>（更新后） 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时，组件DOM已经更新，所以可以执行依赖于DOM的操作。然而在大多数情况下，应该避免在此期间更改状态，因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。<br><strong>beforeDestroy</strong>（销毁前） 在实例销毁之前调用。实例仍然完全可用。<br><strong>destroyed</strong>（销毁后） 在实例销毁之后调用。调用后，所有的事件监听器会被移除，所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。例如进行 removeEventListener, 移除 window 上的自定义对象</p>
<blockquote>
<p>ajax 请求放在哪个生命周期合适？</p>
<p>因为是异步操作，响应返回的时间不确定，如果在响应返回之前就使用到了响应数据可能会引发错误，这样的情况要么提前给响应数据赋值，要么在 mounted 中发起请求。</p>
</blockquote>
<h3 id="vuex-是什么，怎么使用，哪种功能场景使用"><a href="#vuex-是什么，怎么使用，哪种功能场景使用" class="headerlink" title="vuex 是什么，怎么使用，哪种功能场景使用"></a>vuex 是什么，怎么使用，哪种功能场景使用</h3><p>只用来读取的状态集中放在 store 中，改变状态的方法有同步的提交 mutations、异步逻辑 action 。</p>
<p>mutations 定义的方法动态修改 vuex store 中的状态或数据，actions 可以理解为通过将 mutations 里面处理数据的方法变成可异步的处理数据方法，view 层通过 <code>store.dispatch</code> 来分发 action。</p>
<h3 id="vue-中新增自定义模板指令"><a href="#vue-中新增自定义模板指令" class="headerlink" title="vue 中新增自定义模板指令"></a>vue 中新增自定义模板指令</h3><ul>
<li><p>局部指令：</p>
<pre class=" language-vue"><code class="language-vue">var app = new Vue({
    el: '#app',
    data: {},
    // 创建指令(可以多个)
    directives: {
        // 指令名称
        dir1: {
            inserted(el) {
                // 指令中第一个参数是当前使用指令的DOM
                // 对DOM进行操作;
            }
        }
    }
})
</code></pre>
</li>
<li><p>全局指令</p>
<pre class=" language-vue"><code class="language-vue">Vue.directive('dir2', {
    inserted(el) {
        console.log(el);
    }
})
</code></pre>
</li>
</ul>
<p>使用：</p>
<pre class=" language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">v-dir1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">v-dir2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
</code></pre>
<h3 id="vue-自定义过滤器"><a href="#vue-自定义过滤器" class="headerlink" title="vue 自定义过滤器"></a>vue 自定义过滤器</h3><pre class=" language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
     <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>msg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
     {msg| capitalize }
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
</code></pre>
<p>过滤器接收表达式的值 (msg) 作为第一个参数。capitalize 过滤器将会收到 msg的值作为第一个参数。</p>
<ul>
<li><p>局部：</p>
<pre class=" language-vue"><code class="language-vue">var vm=new Vue({
    el:"#app",
    data:{
        msg:''
    },
    filters: {
      capitalize: function (value) {
          // 对数据的处理返回
      }
    }
})
</code></pre>
</li>
<li><p>全局：</p>
<pre class=" language-vue"><code class="language-vue">Vue.filter('capitalize', function (value) {
// 处理数据并返回
})
</code></pre>
</li>
</ul>
<h3 id="keep-alive"><a href="#keep-alive" class="headerlink" title="keep-alive"></a>keep-alive</h3><p>vue 内置的一个抽象组件，页面渲染之后不会被渲染成一个 DOM 元素，可以使被包含的组件保留状态或避免重新渲染。vue 2.1 之后在 keep-alive 上添加两个属性： include（只有名称匹配的组件会被缓存）、exclude（任何名称匹配的组件都不会被缓存），两者都可以用字符串、正则、数组（使用正则、数组要使用 v-bind）。</p>
<p>当组件在keep-alive内被切换时组件的<strong>activated、deactivated</strong>这两个生命周期钩子函数会被执行（因为keep-alive 将组件保存在内存中，并不会销毁以及重新创建，所以不会调用组件的created等方法）</p>
<p>被包裹在keep-alive中的组件的状态将会被保留，例如我们将某个列表类组件内容滑动到第100条位置，那么我们在切换到一个组件后再次切换回到该组件，该组件的位置状态依旧会保持在第100条列表</p>
<p>被keep-alive包裹的动态组件或router-view会缓存不活动的实例，再次被调用这些被缓存的实例会被再次复用，对于我们的某些不是需要实时更新的页面来说大大减少了性能上的消耗，不需要再次发送HTTP请求，但是同样也存在一个问题就是被keep-alive包裹的组件我们请求获取的数据不会再重新渲染页面，这也就出现了例如我们使用动态路由做匹配的话页面只会保持第一次请求数据的渲染结果，所以需要我们在特定的情况下强制刷新某些组件：</p>
<ul>
<li><p>给 router-view 加上 key，保证 key 唯一，即路由切换时都会重新渲染</p>
</li>
<li><p>beforeRouteLeave 钩子函数，根据 to、from、next 进行条件判断强制刷新</p>
<blockquote>
<p>next 有参和无参不同：有参数会将 to.path 改为该参数，然后重新触发该钩子函数。</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>缓存淘汰策略：FIFO（先进先出）、LFU（最少使用）、LRU（最近最少使用）</p>
<p>LRU：如果数据最近被访问过，那么将来被访问的几率也更高，优先淘汰最近没有被访问到的数据</p>
<p>keep-alive 在 vue 2.5.0 版本中新增了 max 属性，表示最多可以缓存多少组件实例，达到这个最大值就会在新实例被创建之前销毁已缓存组件中最久没有被访问到的实例（LRU）</p>
<p><a href="https://gitee.com/JackChu/jd/blob/master/readingNotes-master/vue-source/vue-2.6.11/src/core/components/keep-alive.js" target="_blank" rel="noopener">keep-alive源码</a></p>
<p>在 <code>keep-alive</code> 缓存超过 <code>max</code> 时，使用的缓存淘汰算法就是 LRU 算法，它在实现的过程中用到了 <code>cache</code>对象用于保存缓存的组件实例及 <code>key</code> 值，<code>keys</code> 数组用于保存缓存组件的 <code>key</code> ，当 <code>keep-alive</code> 中渲染一个需要缓存的实例时：</p>
<ul>
<li>判断缓存中是否已缓存了该实例，缓存了则直接获取，并调整 <code>key</code> 在 <code>keys</code> 中的位置（移除 <code>keys</code> 中 <code>key</code> ，并放入 <code>keys</code> 数组的最后一位）</li>
<li>如果没有缓存，则缓存该实例，若 <code>keys</code> 的长度大于 <code>max</code> （缓存长度超过上限），则移除 <code>keys[0]</code>缓存</li>
</ul>
<p><a href="https://zhuanlan.zhihu.com/p/129050567" target="_blank" rel="noopener">参考：从浏览器的缓存淘汰策略和 vue 的keep-alive学习LRU</a></p>
</blockquote>
<h3 id="v-if-与-v-show-区别"><a href="#v-if-与-v-show-区别" class="headerlink" title="v-if 与 v-show 区别"></a>v-if 与 v-show 区别</h3><p>v-if 按照条件决定是否渲染，v-show 是动态设置 DOM 元素的 display 样式属性控制显隐。</p>
<p>v-if 是惰性的，如果初始条件为假则什么也不做，只有在条件第一次变为真时才开始局部编译（编译被缓存后再切换的时候会进行局部卸载），v-show 是在任何条件下（无论条件是否为真）都被编译缓存，而且DOM元素保留。</p>
<p>v-if 有更高的切换消耗，v-show 有更高的初始渲染消耗。</p>
<h3 id="vue-常用修饰符"><a href="#vue-常用修饰符" class="headerlink" title="vue 常用修饰符"></a>vue 常用修饰符</h3><h3 id="vue-中-key-值作用"><a href="#vue-中-key-值作用" class="headerlink" title="vue 中 key 值作用"></a>vue 中 key 值作用</h3><p>key值的作用主要用来减少没必要的diff算法的对比，对于同一个组件或者节点来说，只要父节点状态或属性发生改变，该组件就会进行diff   对比，即使该组件没有发生改变；如果为该组件引入key值，在进行diff对比前先做一次校验，判断该组件是否需要diff对比，也可以判断该   组件是直接更新操作还是销毁或者新建操作，从而提高了diff算法的效率；特别在渲染同级同结构的组件们时，key可以为它们加上了身份的   标志，在rerender时，可以通过key来判断该组件是否已经存在，是否需要跟新或者销毁，新建等操作，提高了diff算法在同级节点上的操作。</p>
<p>原理： 因为在reactelement中有一个属性是key，该属性默认是为空值，所以一般情况下，只要组件不加上key值，react是不会去校验组件   的key，而是直接采用diff算法进行对比，一旦组件加上了key值，react就会在渲染时对该组件的身份进行校验，首先校验新旧组件的key值   是不是一致，不一致的话，该组件直接销毁，然后在新建该组件；如果一致，则比较组件的属性是否发生变化，如果发生变化，则采用diff算   法进行对比，然后得出差异对象，如果属性没发生变化，则认为该组件不需要改</p>
<h3 id="vue-等单页面应用及其优缺点"><a href="#vue-等单页面应用及其优缺点" class="headerlink" title="vue 等单页面应用及其优缺点"></a>vue 等单页面应用及其优缺点</h3><p>spa</p>
<ul>
<li>优点<ul>
<li>速度和响应能力，部分更新，对服务器负载低</li>
<li>用户体验好，无需另开页面实现内容的过渡</li>
<li>易于调试，spa 完成的是整个应用，可以轻松看到完整代码</li>
</ul>
</li>
<li>缺点<ul>
<li>不利于搜索引擎优化（SEO），只有用户交互后才加载内容</li>
<li>路由问题</li>
<li>如果没有进行代码切分或者按路由按需加载，就会影响初始加载时间</li>
</ul>
</li>
</ul>
<p>mpa</p>
<ul>
<li>优点<ul>
<li>利于搜索引擎优化（SEO）</li>
<li>对项目的技术栈没有要求，易于扩展</li>
<li>灵活，分开各个页面维护</li>
</ul>
</li>
<li>缺点<ul>
<li>维护页面或者项目变多</li>
</ul>
</li>
</ul>
<h3 id="vuex-如何实现响应式"><a href="#vuex-如何实现响应式" class="headerlink" title="vuex 如何实现响应式"></a>vuex 如何实现响应式</h3><p>vuex 仅仅作为 vue 的一个插件而存在，只能使用在 vue 上，因为其高度依赖与 vue 的computed 依赖检测系统及插件系统。</p>
<p>工作原理：vuex 中的 store 本质上就是没有 template 的隐藏的 vue 组件。</p>
<p>传入的 state 其实是作为一个隐藏 vue 组件（new Vue）的 data，vuex 的 getters 是借助 vue 的 computed 实现响应式，即 commit 操作本质上是在修改这个组件的 data 值。</p>
<blockquote>
<p>参考</p>
<ul>
<li><a href="https://www.jianshu.com/p/d95a7b8afa06" target="_blank" rel="noopener">vuex 工作原理详解</a></li>
</ul>
</blockquote>
<h3 id="computed-与-methods、watch-的区别"><a href="#computed-与-methods、watch-的区别" class="headerlink" title="computed 与 methods、watch 的区别"></a>computed 与 methods、watch 的区别</h3><h4 id="computed-与-methods"><a href="#computed-与-methods" class="headerlink" title="computed 与 methods"></a>computed 与 methods</h4><ul>
<li>computed是响应式的，methods并非响应式。</li>
<li>调用方式不一样，computed定义的成员像属性一样访问，methods定义的成员必须以函数形式调用。</li>
<li>computed是带缓存的，只有其引用的响应式属性发生改变时才会重新计算，而methods里的函数在每次调用时都要执行。</li>
<li>computed中的成员可以只定义一个函数作为只读属性，也可以定义get/set变成可读写属性，这点是methods中的成员做不到</li>
</ul>
<h4 id="computed-与-watch"><a href="#computed-与-watch" class="headerlink" title="computed 与 watch"></a>computed 与 watch</h4><p>watch 是一个对象，键是 data 对应的数据，值是对应的回调函数。值也可以是方法名，或者包含选项的对象，当 data 的数据发生变化时，就会发生一个回调，他有两个参数，一个 val （修改后的 data 数据），一个 oldVal（原来的 data 数据）。Vue 实例将会在实例化时调用<code>$watch()</code>，遍历 watch 对象的每一个属性</p>
<pre class=" language-javascript"><code class="language-javascript">watch<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    obj<span class="token punctuation">:</span><span class="token punctuation">{</span>
      handler<span class="token punctuation">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>val<span class="token punctuation">,</span> oldVal<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
          console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"obj 变了"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
      deep<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token comment" spellcheck="true">// 该属性设定在任何被侦听的对象的 property 改变时都要执行 handler 的回调，不论其被嵌套多深</span>
      immediate<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token comment" spellcheck="true">// 该属性设定该回调将会在侦听开始之后被立即调用</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>注意：不应该使用箭头函数来定义watcher 函数，因为箭头函数没有 this，它的 this 会继承它的父级函数，但是它的父级函数是 window，导致箭头函数的 this 指向 window，而不是 Vue 实例</p>
<ul>
<li>deep控制是否要看这个对象里面的属性变化</li>
<li>immediate 控制是否在第一次渲染是执行这个函数</li>
</ul>
<p>computed 支持缓存，只有依赖数据发生改变才会重新计算，不支持异步操作。watch 不支持缓存，支持异步操作。</p>
<h3 id="vue-的-scoped-实现原理及穿透"><a href="#vue-的-scoped-实现原理及穿透" class="headerlink" title="vue 的 scoped 实现原理及穿透"></a>vue 的 scoped 实现原理及穿透</h3><p>vue scoped 实现依赖于 postCss 转译，给一个组件的所有 dom 都添加了一个独一无二的 动态属性（data-v-xxxx)，并给 css 选择器添加一个对应的属性选择器，如<code>.example[data-v-xxxx]</code>。</p>
<p>穿透：在 scoped 中通过 <code>&gt;&gt;&gt;</code> 修改其他组件的值。 </p>
<h3 id="vue-文本输出方法"><a href="#vue-文本输出方法" class="headerlink" title="vue 文本输出方法"></a>vue 文本输出方法</h3><ul>
<li>双括号，将元素当成纯文本输出</li>
<li><code>v-html</code>，将元素当成 HTML 标签解析后输出</li>
<li><code>v-text</code>，将元素当成纯文本输出，双括号在网络较慢的情况下会出现不友好的页面显示方式，即数据在加载中会显示双括号，然后加载后显示完整数据，这个过程会有闪烁，v-text 不会出现这种情况</li>
</ul>
<h3 id="vue-set"><a href="#vue-set" class="headerlink" title="vue.$set"></a>vue.$set</h3><p>源码中：对于传入的数据进行改写，如果是 <code>undefined,null,string,symbol,boolean,number</code>等在开发环境下会报错。如果是引用类型：</p>
<ul>
<li>数组：调用 splice 进行修改。数组中的一些方法如 push、shift、splice、sort 等在vue中都通过原型链进行了代理，即vue响应式对象上的数组方法执行时会经过这个代理函数，除了执行本来原型上的方法外，还会进行 <code>this.__ob__.dep.notyfy()</code>进行更新</li>
<li>对象：如果是已存在于对象上的属性直接赋值（这个属性必然已被监听）。如果是新属性则判断这个对象有没有被监听，监听的对象上都会存在 <code>__ob__</code>属性。如果没有被监听直接赋值返回，否则通过 defineReactive 监听赋值。</li>
</ul>
<h3 id="vue-的虚拟-dom-和-diff"><a href="#vue-的虚拟-dom-和-diff" class="headerlink" title="vue 的虚拟 dom 和 diff"></a>vue 的虚拟 dom 和 diff</h3><p>虚拟 dom 就是 js 对象，用操作 js 对象来代替直接操作 dom 节点实现更新，避免大量无谓的计算。将 dom 树转化成虚拟节点 js 对象树，一般虚拟节点构造函数传入 tagName、props、children，render 函数中按照对应的 tagName 进行渲染，一般对于深层子节点递归处理。</p>
<p>diff：diff时调用patch函数，patch接收两个参数<code>vnode，oldVnode</code>，分别代表新旧节点。如果不同类型（包括不同选择器）节点则进行删除旧节点、插入新节点操作，否则进一步判断，之后比较子节点，新旧子节点各有两个头尾变量 startIndex 和 endIndex，两两相互比较，如果有 key 就用 key 进行比较，知道 startIndex &gt; endIndex 说明至少有一个遍历完成，结束比较。</p>
<blockquote>
<p><a href="https://www.cnblogs.com/gxp69/p/11325381.html" target="_blank" rel="noopener">参考 vue  虚拟 dom 和 diff</a></p>
<p><a href="https://www.cnblogs.com/wind-lanyan/p/9061684.html" target="_blank" rel="noopener">详解 vue 的 diff 算法</a></p>
</blockquote>
<h3 id="vue-中同一个页面多次复用组件，对于组件的-data-有什么处理？"><a href="#vue-中同一个页面多次复用组件，对于组件的-data-有什么处理？" class="headerlink" title="vue 中同一个页面多次复用组件，对于组件的 data 有什么处理？"></a>vue 中同一个页面多次复用组件，对于组件的 data 有什么处理？</h3><ul>
<li><p>同一个页面中多次使用同一个组件，如果只创建了一个实例，调用多次就会造成相互干扰的问题，这种解决方法一般是创建多个组件实例分别调用。比如说 </p>
<pre class=" language-js"><code class="language-js"><span class="token keyword">var</span> exOne <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">'./component.vue'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>exTwo <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'./component.vue'</span><span class="token punctuation">)</span>
</code></pre>
</li>
<li><p>vue data 为什么写成函数的形式？组件复用，data 以函数返回值得形式定义，这样每复用一次组件就会返回一个新的 data ，类似于每个组件实例创建一个私有的数据空间，让各个组件实例维护各自的数据。单纯写成对象形式就使得所有组件实例共用了一份 data。</p>
</li>
</ul>
<h3 id="vue-3-0-新特性"><a href="#vue-3-0-新特性" class="headerlink" title="vue 3.0 新特性"></a>vue 3.0 新特性</h3><ul>
<li><p>性能</p>
<ul>
<li>hoistStatic 静态节点提升：当使用hoistStatic时，所有 静态的节点都被提升到render方法之外。这意味着，他们只会在应用启动的时候被创建一次，而后随着每次的渲染被不停的复用。</li>
<li>事件监听缓存：编辑器会为你动态创建一个内联函数，内联函数里面再去饮用当前组件上最新的handler。之后编辑器会将内联函数缓存。每次重新渲染时如果事件处理器没有变，就会使用缓存中的事件处理而不会重新获取事件处理器。</li>
</ul>
</li>
<li><p>composition API</p>
<ul>
<li><p>setup：组件入口，相当于 beforeCreate 与 created 生命周期，返回一个对象，里面所有返回的属性值都会被合并到 vue2.0 render 函数里。接收两个参数：props 和 context，context 即 vue 2 中的 this （可以访问到 root、parent、refs 等）</p>
</li>
<li><p>reactive：处理对象，让它经过 Proxy 加工变成一个响应式对象，深度克隆；toRefs，函数可以将 <code>reactive()</code> 创建出来的响应式对象，转换为普通的对象，只不过，这个对象上的每个属性节点，都是 <code>ref()</code> 类型的响应式数据，配合 <code>v-model</code> 指令能完成数据的双向绑定。</p>
</li>
<li><p>lifecycle hooks：onxxx()</p>
<p><code>beforeCreate</code> -&gt; use <code>setup()
created</code> -&gt; use <code>setup()
beforeMount</code> -&gt; <code>onBeforeMount
mounted</code> -&gt; <code>onMounted
beforeUpdate</code> -&gt; <code>onBeforeUpdate
updated</code> -&gt; <code>onUpdated
beforeDestroy</code> -&gt; <code>onBeforeUnmount
destroyed</code> -&gt; <code>onUnmounted
errorCaptured</code> -&gt; <code>onErrorCaptured</code></p>
</li>
</ul>
<p>Mixin 的问题：可能引入依赖，并不知道页面中的变量混入了什么</p>
</li>
<li><p>fragment：在一个组件中返回多个元素，即组件下根节点可以不止一个。</p>
</li>
<li><p>typescript：vue3源码使用 ts 重写，但并不意味这 vue3 项目也要用 ts，但 vue3 对 ts 有更好的支持</p>
</li>
</ul>
<blockquote>
<p><a href="https://juejin.im/post/6844904017340923917#heading-3" target="_blank" rel="noopener">参考 composition api</a></p>
<p><a href="https://juejin.im/post/6844904199726055437#heading-6" target="_blank" rel="noopener">3.0 新特性</a></p>
</blockquote>
<h3 id="vue-路由钩子"><a href="#vue-路由钩子" class="headerlink" title="vue 路由钩子"></a>vue 路由钩子</h3><ul>
<li><p>beforeEach 和 afterEach ：有全局路由钩子和单个路由钩子两种形式，即可以绑定在 router 对象上或者在 routes 配置中，分别表示会在任意路由跳转前/后或指定路由跳转前后执行。需要执行 next 函数，否则路由不能跳转</p>
</li>
<li><p>组件路由钩子</p>
<p>beforeRouterEnter：在渲染该组件的对应路由被 confirm 之前调用，不能获取组件实例<code>this</code>，函数执行前组件实例还没被创建</p>
<p>beforeRouterUpdate：当前路由改变，但是该组件被复用时调用，会渲染同样的组件则组件实例会被复用，可以访问组件实例<code>this</code></p>
<p>beforeRouteLeave：导航离开该组件的对应路由时调用，可以访问组件实例<code>this</code></p>
</li>
</ul>
<h3 id="vue-与-react-区别"><a href="#vue-与-react-区别" class="headerlink" title="vue 与 react 区别"></a>vue 与 react 区别</h3><ul>
<li>数据是否可变：react 组件的参数通过 props 传入，单向数据流，setState 之后会重新走渲染逻辑。而 vue 的思想是响应式，基于数据可变，对 data 中的每个属性通过 watch 来监听</li>
<li>写法：自定义指令 vs jsx。react 强调 all in js，通过 js 来生成 html，vue 可以把 html、js、css 写在同一个文件中。</li>
<li>往父子组件通信、组件包裹的方向说说</li>
</ul>
<h3 id="vue-mixin-使用与原理"><a href="#vue-mixin-使用与原理" class="headerlink" title="vue mixin 使用与原理"></a>vue mixin 使用与原理</h3><h3 id="vue-router-实现原理"><a href="#vue-router-实现原理" class="headerlink" title="vue-router 实现原理"></a>vue-router 实现原理</h3><p>详情见 <a href="https://jackchu.gitee.io/2019/05/05/vue-router/" target="_blank" rel="noopener">vue router</a></p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2020/09/24/http-next/" rel="next" title="http2 和 http3">
                <i class="fa fa-chevron-left"></i> http2 和 http3
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2020/09/24/react-interview/" rel="prev" title="前端面试总结 react 部分">
                前端面试总结 react 部分 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
    </div>
  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">renhao</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">94</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">19</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#如何理解-vue-双向绑定（响应式原理、依赖收集）"><span class="nav-number">1.</span> <span class="nav-text">如何理解 vue 双向绑定（响应式原理、依赖收集）</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#实现过程"><span class="nav-number">1.1.</span> <span class="nav-text">实现过程</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#属性遍历劫持到对象劫持"><span class="nav-number">1.1.1.</span> <span class="nav-text">属性遍历劫持到对象劫持</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#computed-如何实现响应式"><span class="nav-number">1.2.</span> <span class="nav-text">computed 如何实现响应式</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#vuex-的响应式"><span class="nav-number">1.3.</span> <span class="nav-text">vuex 的响应式</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#对于-MVVM-的理解"><span class="nav-number">2.</span> <span class="nav-text">对于 MVVM 的理解</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-DOM异步更新"><span class="nav-number">3.</span> <span class="nav-text">vue DOM异步更新</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#nexttick-函数"><span class="nav-number">4.</span> <span class="nav-text">nexttick 函数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-的渲染过程（vue-模板到浏览器可以解读的文件过程）"><span class="nav-number">5.</span> <span class="nav-text">vue 的渲染过程（vue 模板到浏览器可以解读的文件过程）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-的生命周期"><span class="nav-number">6.</span> <span class="nav-text">vue 的生命周期</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vuex-是什么，怎么使用，哪种功能场景使用"><span class="nav-number">7.</span> <span class="nav-text">vuex 是什么，怎么使用，哪种功能场景使用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-中新增自定义模板指令"><span class="nav-number">8.</span> <span class="nav-text">vue 中新增自定义模板指令</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-自定义过滤器"><span class="nav-number">9.</span> <span class="nav-text">vue 自定义过滤器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#keep-alive"><span class="nav-number">10.</span> <span class="nav-text">keep-alive</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#v-if-与-v-show-区别"><span class="nav-number">11.</span> <span class="nav-text">v-if 与 v-show 区别</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-常用修饰符"><span class="nav-number">12.</span> <span class="nav-text">vue 常用修饰符</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-中-key-值作用"><span class="nav-number">13.</span> <span class="nav-text">vue 中 key 值作用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-等单页面应用及其优缺点"><span class="nav-number">14.</span> <span class="nav-text">vue 等单页面应用及其优缺点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vuex-如何实现响应式"><span class="nav-number">15.</span> <span class="nav-text">vuex 如何实现响应式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#computed-与-methods、watch-的区别"><span class="nav-number">16.</span> <span class="nav-text">computed 与 methods、watch 的区别</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#computed-与-methods"><span class="nav-number">16.1.</span> <span class="nav-text">computed 与 methods</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#computed-与-watch"><span class="nav-number">16.2.</span> <span class="nav-text">computed 与 watch</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-的-scoped-实现原理及穿透"><span class="nav-number">17.</span> <span class="nav-text">vue 的 scoped 实现原理及穿透</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-文本输出方法"><span class="nav-number">18.</span> <span class="nav-text">vue 文本输出方法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-set"><span class="nav-number">19.</span> <span class="nav-text">vue.$set</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-的虚拟-dom-和-diff"><span class="nav-number">20.</span> <span class="nav-text">vue 的虚拟 dom 和 diff</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-中同一个页面多次复用组件，对于组件的-data-有什么处理？"><span class="nav-number">21.</span> <span class="nav-text">vue 中同一个页面多次复用组件，对于组件的 data 有什么处理？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-3-0-新特性"><span class="nav-number">22.</span> <span class="nav-text">vue 3.0 新特性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-路由钩子"><span class="nav-number">23.</span> <span class="nav-text">vue 路由钩子</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-与-react-区别"><span class="nav-number">24.</span> <span class="nav-text">vue 与 react 区别</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-mixin-使用与原理"><span class="nav-number">25.</span> <span class="nav-text">vue mixin 使用与原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vue-router-实现原理"><span class="nav-number">26.</span> <span class="nav-text">vue-router 实现原理</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">renhao</span>

  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">265.9k</span>
  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  










  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  <script src="//unpkg.com/valine/dist/Valine.min.js"></script>
  
  <script type="text/javascript">
    var GUEST = ['nick','mail','link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item=>{
      return GUEST.indexOf(item)>-1;
    });
    new Valine({
        el: '#comments' ,
        verify: false,
        notify: false,
        appId: '2P1xqUWiCoNm14MH4yhURlJi-gzGzoHsz',
        appKey: 'uJN0uagHIVgXtBO1OuLV9Ban',
        placeholder: 'Just go go',
        avatar:'mm',
        guest_info:guest,
        pageSize:'10' || 10,
    });
  </script>



  





  

  

  

  
  

  

  

  

</body>
</html>
